package rsvp;
/*
 * "kukis-rsvp reader" 
 * is an extension of "My RSVP reader".
 * 
 * Authors:
 * http://www.mscottreynolds.com
 * http://trilulilu.ro/kukiszabolcs
 *
 * Note: I'm thinking to change the license to GPLv3
 *
Copyright (c) 2003, 2004, 2005, M. Scott Reynolds
Copyright (c) 2010, Kuki Szabolcs
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution.
 * Neither the name of M. Scott Reynolds nor the names of its contributors 
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;

import rsvp.util.FileUtility;

/**
 * Add listeners on top or on multiple controls
 * 
 * 
 * TODO implement page-up/page-down on the visible area of the text ticker
 * TODO implement wheel <=> also implement word construct
 * TODO implement bottom and upper text sync (perhaps with scroll?)
 * 
 * 
 * TODO 1 add log file
 * TODO 2 persist settings
 *  - search
 * TODO implement navigation, left and write
 * TODO Recent file list / bookmarks ...?
 * 
 * - Persist settings further, like font, keys, etc.
 * 
 * 2010-04-03 Implement a selection for the whole visible area of the text ticker
 * 
 * @author ksz
 *
 */
public class Main_RSVP extends Applet {
	
	/**
	 * 
	 */
	public static final String DEFAULT_CONFIGURATION_FILE = "configuration.properties" ;
	public static final int WORDS_PER_MINUTE = 300;
	public static final int INCREASE_DECREASE_UNIT = 10;
	public static final int MIN_WORD_LENGTH = 18;
	public final int iTextAreaWidth = 18;
	public int iDisplayNumberOfLines = 8;
	public final Font fontBigDisplay = new Font("Serif", Font.PLAIN, 42);
//	public final Font fontTextDisplay = new Font("Default", Font.BOLD, 16);
	public final Font fontTextDisplay = new Font("Default", Font.PLAIN, 15);

	protected static final String LABEL_BIG_DISPLAY = "bigDisplay";
	protected KeyControll controlBigDisplay = null; 
	
	private static final long serialVersionUID = 7430523465893671712L;
	
	// display controls
	protected Frame win;
	public Button bttStartStop;
	public Button bttPGDown;
	protected TextField wordsPerMinute;
	protected TextField minWordLength;
	private TextField fileLocationField;
	private Button fileLocationLoadButton;
	protected TextArea wordDisplay;
	private TextArea textInput;
	private Label statusLabel;
	
	// for display mechanism
	protected TextTickerThread thread = null;
	protected ReaderStateObject state = new ReaderStateObject();
	
	/**
	 * @param argv
	 * @throws IOException
	 */
	public static void main(String argv[]) throws IOException {
		final Main_RSVP applet = new Main_RSVP();
		applet.loadMain(argv.length > 0 ? argv[0] : null);
	}

	protected void loadMain(String sFileName)
	{		
		win = new Frame("RSVP");
		init();
		win.add(this);
		win.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				win.dispose();
				System.exit(0);
			}			
		});
		win.addComponentListener(wordComponentChange());
		win.addKeyListener(new KeyControll(this, "applet"));
		
		win.pack();
		win.setVisible(true);
		
		if (sFileName != null) {		
			fileLocationField.setText(sFileName);
			doLoadFile();
		} 
				
		start();
	}

	/**
	 * @return
	 */
	public TextArea getWordDisplay() {
		return wordDisplay;
	}

	public TextArea getTextInput() {
		return textInput;
	}

	public Label getStatusLabel() {
		return statusLabel;
	}

	/** 
	 * Do applet initialization
	 * 
	 * @see java.applet.Applet#init()
	 */
	public void init() {
		setLayout(new BorderLayout());

		// Setup the toolbar
		Panel toolBar = new Panel();
		toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
		bttStartStop = new Button("Start");
		bttStartStop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				doStartStop();
				textInput.requestFocus();
				textInput.requestFocusInWindow();
			}
		});
		toolBar.add(bttStartStop);

		bttPGDown = new Button("Next");
		bttPGDown.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				callPageDown();
				textInput.requestFocus();
				textInput.requestFocusInWindow();
			}
		});
		
		toolBar.add(bttPGDown);
		toolBar.add(new Label("Max WPM:"));
		wordsPerMinute = new TextField(5);
		wordsPerMinute.setText(String.valueOf(WORDS_PER_MINUTE));
		wordsPerMinute.setBackground(Color.white);
		wordsPerMinute.addComponentListener(wordComponentChange());
		
		toolBar.add(wordsPerMinute);

		toolBar.add(new Label("Min Word Length:"));
		minWordLength = new TextField(2);
		minWordLength.setText(String.valueOf(MIN_WORD_LENGTH));
		minWordLength.setBackground(Color.white);
		toolBar.add(minWordLength);
		
		fileLocationField = new TextField(30);
		fileLocationField.setBackground(Color.white);
		fileLocationLoadButton = new Button("Load");
		fileLocationLoadButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				doLoadFile();
			}
		});

		boolean bEnableFileLoad = false;
		if (bEnableFileLoad) {
			toolBar.add(new Label("Load:"));
			toolBar.add(fileLocationField);			
			toolBar.add(fileLocationLoadButton);
		}
				
		add(toolBar, BorderLayout.NORTH);

		// Setup the bottom panel
		Panel bottomPanel = new Panel();
		bottomPanel.setLayout(new GridLayout(1, 2));
		wordDisplay = new TextArea("", iDisplayNumberOfLines +1, iTextAreaWidth, TextArea.SCROLLBARS_VERTICAL_ONLY);
		wordDisplay.setFont(fontBigDisplay);
		final int backgroundColor = 75;
		final int forgroundColor =  175;
		wordDisplay.setBackground(new Color(0, backgroundColor, 0));
		final Color color2 = new Color(150, 180, 220);
		wordDisplay.setForeground(color2);
		// wordDisplay.setBackground(Color.white);
		// wordDisplay.setForeground(Color.black);
//		wordDisplay.setAlignment(Label.LEFT);		
		wordDisplay.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent evt) {
				callPageDown();

				textInput.requestFocus();
				textInput.requestFocusInWindow();
				System.out.println("requested focus in Window mousePressed");

			}
			
			public void mouseReleased(MouseEvent evt) { 				
				textInput.requestFocus();
				textInput.requestFocusInWindow();
				System.out.println("requested focus in Window mouseReleased");
			}
			
			public void mouseClicked(MouseEvent evt) { 				
				textInput.requestFocus();
				textInput.requestFocusInWindow();
				System.out.println("requested focus in Window mouseClicked");
			}
			
		});
		controlBigDisplay = new KeyControll(this, LABEL_BIG_DISPLAY);
		wordDisplay.addKeyListener(controlBigDisplay);
		
		
		textInput = new TextArea("", iDisplayNumberOfLines, iTextAreaWidth-10, TextArea.SCROLLBARS_VERTICAL_ONLY);
		textInput.setBackground(Color.white);
		textInput.setFont(fontTextDisplay);
		final Color color1 = new Color(forgroundColor, forgroundColor, forgroundColor);
		textInput.setBackground(color1);
		textInput.addKeyListener(new KeyControll(this, "textInput"));
		
		bottomPanel.add(textInput);	
		bottomPanel.add(wordDisplay);
		
		add(bottomPanel, BorderLayout.CENTER);

		// Create the status label
		statusLabel = new Label("Ready");
		add(statusLabel, BorderLayout.SOUTH);
	}

	/**
	 * @return
	 */
	private ComponentListener wordComponentChange()
	{
		return new ComponentListener()
		{	
			/* (non-Javadoc)
			 * @see java.awt.event.ComponentListener#componentResized(java.awt.event.ComponentEvent)
			 */
			public void componentResized(ComponentEvent e)
			{
//				System.out.println("componentResized");
				resizeWord();		
			}

			public void componentHidden(ComponentEvent e)
			{		
//				System.out.println("componentHidden(ComponentEvent e)");
			}

			public void componentMoved(ComponentEvent e)
			{
//				System.out.println("componentMoved");
			}

			
			public void componentShown(ComponentEvent e)
			{
//				System.out.println("componentShown");
			}
		};
	}

	
	/** 
	 * When this is run as an Applet and is initialized, this will be called.
	 * @see java.applet.Applet#start()
	 */
	public void start() {
		try { Thread.sleep(200); } catch (InterruptedException e) {}
		if (textInput.getText().length() < 1)
			textInput.setText("Paste some text in the left window ");

		textInput.setCaretPosition(0);
		doStartStop();
	}


	/**
	 * Start/Stop the display of text.
	 */
	public synchronized void doStartStop()
	{
		if (isStarted())
			doStop_ifStarted();
		else
			doStart_ifStopped();
	}

	/**
	 * 
	 */
	public void doStop_ifStarted()
	{
		if (isStarted())
		{			
			updateControls(true);

			// exit from this function only after thread is indeed stopped 
			if (null != thread) {
				try	{ 
//					System.out.println("Joining thread: " + thread);
					thread.join(1000);
					Thread.yield();
					thread.join(15*1000);
//					System.out.println(" Thread joined: " + thread);
				} catch (Exception e)	{}
			}
			thread = null;
		}
	}

	/**
	 * 
	 */
	protected void doStart_ifStopped()
	{
		if (!isStarted())
		{
			int wpm = parseIntDef(wordsPerMinute.getText(), WORDS_PER_MINUTE);
			wordsPerMinute.setText(String.valueOf(wpm));
			int min = parseIntDef(minWordLength.getText(), MIN_WORD_LENGTH);
			minWordLength.setText(String.valueOf(min));
			thread = new TextTickerThread(this);
			thread.start();
			updateControls(false);
		}
	}

	/**
	 * @return
	 */
	public boolean isStarted()
	{	
		return this.bttStartStop.getLabel().equalsIgnoreCase("Stop");
	}

	
	private void resizeWord()
	{
		doStop_ifStarted();
		final FontMetrics fontMetrics = wordDisplay.getFontMetrics(wordDisplay.getFont());
		this.iDisplayNumberOfLines = (wordDisplay.getHeight() - 20) / fontMetrics.getHeight();
//		this.iDisplayNumberOfLines -= 1;
		System.out.println("iDisplayNumberOfLines = " + iDisplayNumberOfLines);
		
//		System.out.println("getScrollbarVisibility()  = " + wordDisplay.getHeight()/fontMetrics.getHeight());
//		System.out.println("wordDisplay.getHeight() = " + wordDisplay.getHeight());
//		;

	}			

	/**
	 * 
	 */
	private void doLoadFile()
	{
		doStop_ifStarted();
		String sFileName = fileLocationField.getText();
		
		try
		{
			final File fToLoad = new File(sFileName);
//			String sFileContent = FileUtility.readFile(fToLoad, "ANSI");
			String sFileContent = FileUtility.readFile(fToLoad, "UTF-8");
			win.setTitle("RSVP: " + fToLoad.getName());
			sFileContent = fixText(sFileContent);
			textInput.setText(sFileContent);
			
			
//			final String charsetName = "UTF8";
//			final String charsetName = "ISO-8859-1";
//			byte[] utf8Bytes = original.getBytes(charsetName);
////			byte[] defaultBytes = original.getBytes();
	//
//			String roundTrip = new String(utf8Bytes, charsetName);
			
//			int iPos = sFileContent.indexOf("not even beyond the person");
//			textInput.setCaretPosition(iPos);
//			String sAnalisys = sFileContent.substring(iPos, iPos + 100);
//			final char[] charArray = sAnalisys.toCharArray();
//			final int iLookFor = 26;
//			System.out.println(charArray[iLookFor]);
//			System.out.println((int)charArray[iLookFor]);
//			System.out.println(sAnalisys);
//			System.out.println(charArray);
		} catch (IOException e)
		{
//			e.printStackTrace();
			statusLabel.setText(e.getMessage() + " " + e.toString());
		}
	}
	
	/**
	 * @param value
	 * @param defaultValue
	 * @return
	 */
	public int parseIntDef(String value, int defaultValue) {
		try {
			return Integer.parseInt(value);
		} catch (Exception e) {
			return defaultValue;
		}
	}

	/**
	 * Update the controls.
	 * 
	 * @param stopped
	 */
	public void updateControls(boolean stopped) {
		bttStartStop.setLabel(stopped ? "Start" : "Stop");
		wordsPerMinute.setEnabled(stopped);
		minWordLength.setEnabled(stopped);
		textInput.setEditable(stopped);	
		fileLocationField.setEditable(stopped);
		fileLocationLoadButton.setEnabled(stopped);
//		statusLabel.setText(stopped ? "Ready" : "Displaying at "
//				+ wordsPerMinute.getText() + " words per minute...");
		// if (stopped)
		// thread = null;
	}

	/**
	 * @return the fileLocationLoadButton
	 */
	public String getReadFileLocation()
	{
		return fileLocationField.getText();
	}


	/**
	 * @param sFileContent
	 * @return
	 */
	public static String fixText(String sFileContent)
	{
		String sFixedText = sFileContent.replaceAll("\r", "");
		sFixedText = sFixedText.replaceAll("\n *\t* *\n", "\n\n");
		sFixedText = sFixedText.replaceAll("\t\n", "\n");
//		sFixedText = sFixedText.replaceAll(" *\n", "\n");
		sFixedText = sFixedText.replaceAll(" *\n", "\n\n");
		sFixedText = sFixedText.replaceAll("\n*\n\n", "\n\n");

		if (!sFixedText.endsWith(" "))
			sFixedText += " ";
			
		return sFixedText;
	}

	/**
	 * 
	 */
	public void callPageDown()
	{
		doStop_ifStarted();
		
		TextTickerThread ttt = new TextTickerThread(this);
		ttt.pageDown();				
	}
}

/**
 * @author ksz
 *
 */
class KeyControll implements KeyListener
{
	/**
	 * Referring parent object
	 */
	Main_RSVP parent; 
	boolean m_isBigDisplay = false;

	/**
	 * @param parent
	 * @param sSourceName 
	 */
	public KeyControll(Main_RSVP parent, String sSourceName)
	{
		super();
		this.parent = parent;
		this.m_isBigDisplay = Main_RSVP.LABEL_BIG_DISPLAY.equals(sSourceName);
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
	 */
	public void keyTyped(KeyEvent e)
	{		
//		System.out.println("keyTyped: e.getKeyChar(): " + e.getKeyChar());							
//		System.out.println("keyTyped: e.getKeyCode(): " + e.getKeyCode());							
		final int keyChar = (int)e.getKeyChar();

//		<CTRL>-N - sentence back 14
		final int iCTRL_N = 14;
		if (iCTRL_N == keyChar)
			parent.callPageDown();

//		<CTRL>-U - sentence forward 21 
//		<CTRL>-M - paragraph previous 10
//		<CTRL>-H - paragraph next 8				
		
//		<CTRL>-S - start stop 19
		final int iCTRL_S = 19;
		if (iCTRL_S == keyChar)
			parent.doStartStop();

//		<CTRL>-G - faster 7
		final int iCTRL_G = 7;
		if (iCTRL_G == keyChar)
			increaseSpeed();
//		<CTRL>-R - slower 18
		final int iCTRL_R = 18;
		if (iCTRL_R == keyChar)
			decreaseSpeed();		
	}

	
	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	public void keyReleased(KeyEvent e)
	{
//		System.out.println("keyReleased: e.getKeyChar(): " + e.getKeyChar());							
//		System.out.println("keyReleased: e.getKeyCode(): " + e.getKeyCode());							
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
	 */
	public void keyPressed(KeyEvent e)
	{
//		System.out.println("keyPressed: e.getKeyChar(): " + e.getKeyChar());							
//		System.out.println("keyPressed: e.getKeyCode(): " + e.getKeyCode());							

		final int keyCode = (int)e.getKeyCode();

		final int iPageUp = 33;
		if (iPageUp == keyCode && m_isBigDisplay)
			callPageUp();
		
		final int iPageDown = 34;
		if (iPageDown == keyCode && m_isBigDisplay)
			callPageDown();

	}

	/**
	 * 
	 */
	protected void callPageDown()
	{
		parent.callPageDown();
	}

	/**
	 * @throws InterruptedException 
	 * 
	 */
	protected void callPageUp() 
	{
		TextTickerThread ttt = new TextTickerThread(parent);
		ttt.pageUp();		
	}
	
	/**
	 * 
	 */
	private void increaseSpeed()
	{
		int iSpeed = Integer.parseInt(parent.wordsPerMinute.getText());
		iSpeed += Main_RSVP.INCREASE_DECREASE_UNIT;
		parent.wordsPerMinute.setText("" + iSpeed);

		if (parent.isStarted()) {
			parent.doStartStop();	//stop
			parent.doStartStop();	//start
		}
		
	}
	
	/**
	 * 
	 */
	private void decreaseSpeed()
	{
		int iSpeed = Integer.parseInt(parent.wordsPerMinute.getText());
		iSpeed -= Main_RSVP.INCREASE_DECREASE_UNIT;
		parent.wordsPerMinute.setText("" + iSpeed);
		if (parent.isStarted()) {
			parent.doStartStop();	//stop
			parent.doStartStop();	//start
		}		
	}

}